
// Main header
#include "../Main.h"

//My Header
#include "Preset.h"

// Fridge
#include "Element.h"
#include "../FileTools.h"
#include "../FArchive.h"
#include "../TextureManager.h"
#include "../Visuals/Visuals_Main.h"

// External
#include <string.h>
#include <stdio.h>

static const u_int uFRIDGE_FILE_CHUNK = 16384;
static const u_int uFRIDGE_FILE_HEADER = 0x00F71D6E;
static const u_int uFRIDGE_FILE_VERSION = 1;

Preset::Preset()
{
	m_pxTextureManager = new TextureManager();	
	m_pxElements = new SuperTree< Element >( true );
	m_pxResources = new TypedList< Resource >( true );
	m_szFullPath[ FRIDGE_MAX_PATH ] = 0;
	m_szFileName[ FRIDGE_MAX_PATH ] = 0;
    strncpy( m_szFileName, "New Preset.fridge", FRIDGE_MAX_PATH	);
	m_szWorkingDirectory[ FRIDGE_MAX_PATH ] = 0;
	m_bNeedsInit = false;
    m_pxArchive = new FArchive( m_szFileName );
}

Preset::~Preset()
{
	delete m_pxTextureManager;
	m_pxTextureManager = 0;
	
	delete m_pxElements;
	m_pxElements = 0;
	
	delete m_pxResources;
	m_pxResources = 0;

    delete m_pxArchive;
    m_pxArchive = 0;
	
	FileTools::RemoveTempDirectory();
}

void Preset::Clear()
{
	delete m_pxTextureManager;
	m_pxTextureManager = new TextureManager();
	
	delete m_pxElements;
	m_pxElements = new SuperTree< Element >( true );
	
	delete m_pxResources;
	m_pxResources = new TypedList< Resource >( true );
	
	CreateWorkingFiles();
}

void Preset::CreateWorkingFiles()
{
    // Make temporary directories
    FileTools::MakeDirectory( FRIDGE_DIR_TEMPORARY_APP );
    FileTools::MakeDirectory( FRIDGE_DIR_TEMPORARY_PRESET );
    FileTools::MakeDirectory( FRIDGE_DIR_TEMPORARY_RESOURCES );

    // Make temporary files
    FileTools::MakeTemporaryFile( FRIDGE_INTERNAL_PRESET );
    FileTools::MakeTemporaryFile( FRIDGE_INTERNAL_RESOURCE_LIST );
}

bool Preset::LoadFromFile( const char* szFilename )
{
	// Set file name without path
	strcpy( m_szFileName, &(strrchr( szFilename, '/' )[1]) );
	
	// Set working directory
    FileTools::GetTempDirectory( FRIDGE_DIR_TEMPORARY_APP, m_szWorkingDirectory );
    sprintf( m_szWorkingDirectory, "%s/%s", m_szWorkingDirectory, m_szFileName );

	// Extract preset files
    m_pxArchive = new FArchive( szFilename );
    m_pxArchive->Extract();
	
	// Open preset data file
	char szWorkingFilename[ FRIDGE_MAX_PATH ];
    sprintf( szWorkingFilename, "%s/%s", m_szWorkingDirectory, FileTools::szFRIDGE_INTERNAL_PRESET_FILE );
	
	FILE* pxFile = 0;
    pxFile = fopen( szWorkingFilename, "rb" );
	
	if( !pxFile )
	{
		return false;
	}
	
	u_int uHdr = 0;
	FileTools::ReadFromFile( uHdr, pxFile );
	if( uHdr != uFRIDGE_FILE_HEADER )
	{
		fclose( pxFile );
		return false;
	}
	
	u_int uVersion = u_int(-1);
	FileTools::ReadFromFile( uVersion, pxFile ); 
	if( uVersion > uFRIDGE_FILE_VERSION )
	{
		fclose( pxFile );
		return false;
	}
	
	// Read elements, format is...
	// 4: FHandle (INVALID_HANDLE) is end
	// 4: Parent FHandle
	// n: Element
	
	FHandle xHandle = INVALID_HANDLE;
	for(;;)
	{
		FileTools::ReadFromFile( xHandle, pxFile );
		if( xHandle == INVALID_HANDLE )
		{
			break;
		}
		FHandle xParent;
		FileTools::ReadFromFile( xParent, pxFile );
		Element* pxElement = new Element();
		if( !pxElement->ReadFromFile( pxFile ) )
		{
			fclose( pxFile );
			return false;
		}
		m_pxElements->Add( pxElement, xParent, xHandle );
	}
	
	fclose( pxFile );
	
	// TODO: Load resources from resource file
	
	return false;
}

bool Preset::SaveToFile( const char* const szFilename ) const
{
	// TODO: Error checking
	
	// Write preset data to data file in the working directory
    char szPresetDataFilename[ FRIDGE_MAX_PATH ];
    char szWorkingDirectory[ FRIDGE_MAX_PATH ];
    FileTools::GetTempDirectory( FRIDGE_DIR_TEMPORARY_PRESET, szWorkingDirectory );
    sprintf( szPresetDataFilename, "%s%s", szWorkingDirectory, FileTools::szFRIDGE_INTERNAL_PRESET_FILE );

    FridgeLogf("%s", szPresetDataFilename);

	FILE* pxFile = 0;
	pxFile = fopen( szPresetDataFilename, "wb" );
	if( !pxFile )
	{
		return false;
	}
	
	FileTools::WriteToFile( uFRIDGE_FILE_HEADER, pxFile );
	FileTools::WriteToFile( uFRIDGE_FILE_VERSION, pxFile );
	
	if( !m_pxElements )
	{
		fclose( pxFile );
		return false;
	}
	SuperTreeWalker< Element > xWalker( m_pxElements );
	
	// Write elements, format is..
	// 4: FHandle (INVALID_HANDLE) is end
	// 4: Parent FHandle
	// n: Element
	
	FHandle xHandle;
	while( !xWalker.Done() )
	{
		xHandle = xWalker.GetHandle();
		FileTools::WriteToFile( xHandle, pxFile );
		xHandle = m_pxElements->FindParent( xHandle );
		FileTools::WriteToFile( xHandle, pxFile );
		
		Element* pxElement = xWalker.Get( true );
		if( pxElement )
		{
			pxElement->SaveToFile( pxFile );
		}
	}
	
	xHandle = INVALID_HANDLE;
	FileTools::WriteToFile( xHandle, pxFile );
	
	fclose( pxFile );
	
	// Copy current working directory name
	char szOldDirectoryName[ FRIDGE_MAX_PATH ];
    strncpy( szOldDirectoryName, szWorkingDirectory, FRIDGE_MAX_PATH );
	
	strncpy( m_szFullPath, szFilename, FRIDGE_MAX_PATH );
	m_szFullPath[ FRIDGE_MAX_PATH - 1 ] = '\0';
	
	// Get file name without path
	strcpy( m_szFileName, &(strrchr( m_szFullPath, '/' )[1]) );
	
	// Rename working directory to the new filename
	char szNewDirectoryName[ FRIDGE_MAX_PATH ];
    //char szTempDir[ FRIDGE_MAX_PATH ];
    FileTools::GetTempDirectory( FRIDGE_DIR_TEMPORARY_APP, szNewDirectoryName );
    sprintf( szNewDirectoryName, "%s/%s", szNewDirectoryName, m_szFileName );

    FridgeLogf("RENAME: %s", szNewDirectoryName);
	FileTools::RenameDirectory( szOldDirectoryName, szNewDirectoryName );
	
	// Set working directory to the new name
    strncpy( szWorkingDirectory, szNewDirectoryName, FRIDGE_MAX_PATH );

    // Write archive
    m_pxArchive->WriteToDirectory( m_szFullPath );
	
	return true;
}

FHandle Preset::CreateElement( const FFileInfo* pxFFileInfo, const FHandle uParent /*= TREE_HANDLE_HEAD*/, const FHandle uInsertAfter /*= INVALID_HANDLE*/ )
{
	Element* pxElement = new Element();
	
	if( pxElement->ReadFromTemplateFile( pxFFileInfo->GetFullPathAndName() ) )
	{
		return m_pxElements->InsertAfter( pxElement, uParent, uInsertAfter );
	}
	
	return INVALID_HANDLE;
}


FHandle Preset::CreateElement( const Element* pxCopyFromElement, const FHandle uParent /*= TREE_HANDLE_HEAD*/, const FHandle uInsertAfter /*= INVALID_HANDLE*/ )
{
	if( !pxCopyFromElement )
	{
		return INVALID_HANDLE;
	}
	
	Element* pxElement = new Element( pxCopyFromElement );
	
	return m_pxElements->InsertAfter( pxElement, uParent, uInsertAfter );
}

FHandle Preset::CreateResource( const FFileInfo* pxFFileInfo )
{
	// WRITE ME
	return 0;
}

FHandle Preset::CreateResource( const Resource* pxCopyFromResource )
{
	// WRITE ME
	return 0;
}

void Preset::RemoveElement( const FHandle uElement )
{
	m_pxElements->Delete( uElement );
}

void Preset::RemoveResource( const FHandle uResource )
{
	m_pxResources->Delete( uResource );
}

void Preset::MoveElement( const FHandle uElement, const FHandle uParent, const FHandle uInsertAfter )
{
	m_pxElements->Move( uElement, uParent, uInsertAfter );
}

Element* Preset::GetElement( const FHandle uElement )
{
	return m_pxElements->Get( uElement );
}

Resource* Preset::GetResource( const FHandle uResource )
{
	return m_pxResources->Get( uResource );
}

void Preset::Init()
{	
	SuperTreeWalker< Element > xWalker( m_pxElements );
	
	while( !xWalker.Done() )
	{
		Element* pxElem = xWalker.Get( true );
		if( pxElem )
		{
			pxElem->Init();
		}
	}
	m_bNeedsInit = false;
}

void Preset::Update()
{
	if( m_bNeedsInit )
	{
		Init();
	}
	
	SuperTreeWalker< Element > xWalker( m_pxElements );
	
	while( !xWalker.Done() )
	{
		Element* pxElem = xWalker.Get( true );
		if( pxElem )
		{
			pxElem->Update();
		}
	}
}

void Preset::Render()
{
	RenderInternal_Recursive( TREE_HANDLE_HEAD );
}

void Preset::RenderInternal_Recursive( FHandle xHead )
{
	SuperTreeWalker< Element > xWalker( m_pxElements, xHead );
	xWalker.SetShouldRecurseCallback( SuperTreeWalker<Element>::GetNoRecursionCallback() );
	
	while( !xWalker.Done() )
	{
		Element* pxElem = xWalker.Get();
		if( pxElem )
		{
			if( pxElem->IsGroup() )
			{
				Visuals_Main::PushRenderTarget();
				Preset::RenderInternal_Recursive( xWalker.GetHandle() );
				Visuals_Main::PopRenderTarget();
			}
			pxElem->Render();
		}
		xWalker.Next();
	}
}

SuperTree<Element>* Preset::GetElements()
{
	return m_pxElements;
}

const SuperTree<Element>* Preset::GetElements() const
{
	return m_pxElements;
}

TypedList< Resource >* Preset::GetResources()
{
	return m_pxResources;
}

const TypedList< Resource >* Preset::GetResources() const
{
	return m_pxResources;
}


// eof
